package com.zhanglijie.improve.collections.copyonwrite;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.RandomAccess;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantLock;

import javax.lang.model.element.NestingKind;

/**
 * @author zhanglijie
 * @version 1.0
 * @since 1.1.0 2022/12/1 0001 0:10
 */
public class MyCopyOnWriteArrayList<E> implements List<E>, RandomAccess,Cloneable, Serializable{
   //用来加在写操作上的锁
    final ReentrantLock lock = new ReentrantLock();

    public Object[] getArray() {
        return array;
    }

    public void setArray(Object[] array) {
        this.array = array;
    }

    //底层数组的指针
    private volatile Object[] array;

    public MyCopyOnWriteArrayList(){
        setArray(new Object[0]);
    }

    /**
     * get 操作
     * @param index
     * @return
     */
    @Override
    public E get(int index) {
        return get(getArray(),index);
    }


    private E get(Object[] a,int index){
        return (E) a[index];
    }

    /**
     * set 操作
     * @param index
     * @param element
     * @return
     */
    @Override
    public E set(int index, E element) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try{
            Object[] elements = getArray();
            //先获取旧值
            E oldValue = get(elements, index);
            if(oldValue != element){
                //复制到新数组
                int length = elements.length;
                Object[] newElements = Arrays.copyOf(elements, length);
                //在新数组上的index上加上element元素
                newElements[index] = element;
                setArray(newElements);
            }else {
                //重新设置一遍 为什么既然旧值和新值相同为啥将旧数组还要重新set一下
                //这里涉及到happen-before原子，为了get方法能看到最新的值
                setArray(elements);
            }
        }finally {
            lock.unlock();
        }
        return null;
    }

    /**
     * 在数组末尾新增一个元素
     * @param e
     * @return
     */
    @Override
    public boolean add(E e) {
        ReentrantLock lock = this.lock;
        lock.lock();
        try{
            //先获取旧数组
            Object[] elements = getArray();
            int length = elements.length;
            //拷贝一个新数组 新数组容量比旧数组长度多一  新数组包含旧数组元素
            Object[] newElements = Arrays.copyOf(elements, length + 1);
            newElements[length] = e;
            //最后移动array指针
            setArray(newElements);
            return true;
        }finally {
            lock.unlock();
        }
    }



    /**
     * 在数组上指定坐标新增一个元素
     * @param index
     * @param element
     */
    @Override
    public void add(int index, E element) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            if (index > len || index < 0)
                throw new IndexOutOfBoundsException("Index: "+index+
                    ", Size: "+len);
            Object[] newElements;
            int numMoved = len - index;
            if (numMoved == 0)
                newElements = Arrays.copyOf(elements, len + 1);
            else {
                newElements = new Object[len + 1];
                System.arraycopy(elements, 0, newElements, 0, index);
                System.arraycopy(elements, index, newElements, index + 1,
                    numMoved);
            }
            newElements[index] = element;
            setArray(newElements);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 添加一个集合
     * @param c
     * @return
     */
    @Override
    public boolean addAll(Collection<? extends E> c) {
        Object[] cs = (c.getClass() == MyCopyOnWriteArrayList.class) ?
            ((MyCopyOnWriteArrayList<?>)c).getArray() : c.toArray();
        if (cs.length == 0)
            return false;
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            if (len == 0 && cs.getClass() == Object[].class)
                setArray(cs);
            else {
                Object[] newElements = Arrays.copyOf(elements, len + cs.length);
                System.arraycopy(cs, 0, newElements, len, cs.length);
                setArray(newElements);
            }
            return true;
        } finally {
            lock.unlock();
        }
    }

    /**
     * Appends the element, if not present.
     *
     * @param e element to be added to this list, if absent
     * @return {@code true} if the element was added
     */
    public boolean addIfAbsent(E e) {
        Object[] elements = getArray();
        return indexOf(e, elements, 0, elements.length) >= 0 ? false :
            addIfAbsent(e, elements);
    }
    private boolean addIfAbsent(E e, Object[] element) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] newestArray = getArray();
            int len = newestArray.length;
            // 存在别的线程提前做了add操作
            if (element != newestArray) {

                int common = Math.min(element.length, len);
                //别的线程执行add末尾
                for (int i = 0; i < common; i++) {
                    //做内容比较不做地址比较
                    if (newestArray[i] != element[i] && eq(e, newestArray[i])) {
                        return false;
                    }
                }
                //别的线程执行add的下标在之前e元素之前位置导致e元素后移了，所以到后部分去查找
                if (indexOf(e, newestArray, common, len) >= 0){
                    return false;
                }
            }
            Object[] newElements = Arrays.copyOf(newestArray, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }
    private static boolean eq(Object o1, Object o2) {
        return (o1 == null) ? o2 == null : o1.equals(o2);
    }
    private static int indexOf(Object o, Object[] elements,
        int index, int fence) {
        if (o == null) {
            for (int i = index; i < fence; i++)
                if (elements[i] == null)
                    return i;
        } else {
            for (int i = index; i < fence; i++)
                if (o.equals(elements[i]))
                    return i;
        }
        return -1;
    }

    /**
     * 指定位置的元素从数组中移除
     * @param index
     * @return
     */
    @Override
    public E remove(int index) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            E oldValue = get(elements, index);
            int numMoved = len - index - 1;
            if (numMoved == 0)
                setArray(Arrays.copyOf(elements, len - 1));
            else {
                Object[] newElements = new Object[len - 1];
                System.arraycopy(elements, 0, newElements, 0, index);
                System.arraycopy(elements, index + 1, newElements, index,
                    numMoved);
                setArray(newElements);
            }
            return oldValue;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 移除某个元素
     * @param o
     * @return
     */
    @Override
    public boolean remove(Object o) {
        Object[] elements = getArray();
        int index = indexOf(o, elements, 0, elements.length);
        return (index < 0) ? false : remove(o, elements, index);
    }

    private boolean remove(Object o, Object[] snapshot, int index) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] current = getArray();
            int len = current.length;
            if (snapshot != current) findIndex: {
                int prefix = Math.min(index, len);
                for (int i = 0; i < prefix; i++) {
                    if (current[i] != snapshot[i] && eq(o, current[i])) {
                        index = i;
                        break findIndex;
                    }
                }
                if (index >= len)
                    return false;
                if (current[index] == o)
                    break findIndex;
                index = indexOf(o, current, index, len);
                if (index < 0)
                    return false;
            }
            Object[] newElements = new Object[len - 1];
            System.arraycopy(current, 0, newElements, 0, index);
            System.arraycopy(current, index + 1,
                newElements, index,
                len - index - 1);
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }
    @Override
    public int size() {
        return getArray().length;
    }

    @Override
    public boolean isEmpty() {
        return getArray().length == 0;
    }

    @Override
    public boolean contains(Object o) {
        return false;
    }

    @Override
    public Iterator<E> iterator() {
        return null;
    }

    @Override
    public Object[] toArray() {
        return new Object[0];
    }

    @Override
    public <T> T[] toArray(T[] a) {
        return null;
    }




    @Override
    public boolean containsAll(Collection<?> c) {
        return false;
    }



    @Override
    public boolean addAll(int index, Collection<? extends E> c) {
        return false;
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        return false;
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        return false;
    }

    @Override
    public void clear() {

    }





    @Override
    public int indexOf(Object o) {
        return 0;
    }

    @Override
    public int lastIndexOf(Object o) {
        return 0;
    }

    @Override
    public ListIterator<E> listIterator() {
        return null;
    }

    @Override
    public ListIterator<E> listIterator(int index) {
        return null;
    }

    @Override
    public List<E> subList(int fromIndex, int toIndex) {
        return null;
    }
}
